
#include <BOpenGL>
#include <BRender>
#include <BCamera>
#include <BTexture>

#include <BTextual>
#include "member_BTextual.h"
#include "member_BTexture.h"

using namespace BWE;

#define member					(*(member_BTextual*)_ptr)
#define member_allocate()		_ptr = new member_BTextual(this)
#define member_release()		delete (member_BTextual*)_ptr

BTextual::BTextual()
{
	member_allocate();
}
BTextual::BTextual(BReal size)
{
	member_allocate();
	member.size.set(size, size);
}
BTextual::BTextual(BReal width, BReal height)
{
	member_allocate();
	member.size.set(width, height);
}
BTextual::BTextual(const BTextual& other)
{
	member_allocate();
	this->copy(&other);
}
BTextual::~BTextual()
{
	member_release();
}

void BTextual::copy(const BShape* other)
{
	BShape::copy(other);
	if (const BTextual* textual = dynamic_cast<const BTextual*>(other))
	{

	}
}
BShape* BTextual::clone() const
{
	return new BTextual(*this);
}

void BTextual::setSize(BReal width, BReal height)
{
	setSize(BRealSize(width, height));
}
void BTextual::setSize(const BRealSize& size)
{
	if (member.size != size)
	{
		member.size = size;
		this->dirty();
	}
}
const BRealSize& BTextual::size()
{
	return member.size;
}

void BTextual::setRight(BReal x, BReal y, BReal z)
{
	setRight(BVector(x, y, z));
}
void BTextual::setRight(const BVector& right)
{
	if (member.right != right)
	{
		member.right = right;
		this->dirty();
	}
}
const BVector& BTextual::right() const
{
	return member.right;
}

void BTextual::setDown(BReal x, BReal y, BReal z)
{
	setDown(BVector(x, y, z));
}
void BTextual::setDown(const BVector& down)
{
	if (member.down != down)
	{
		member.down = down;
		this->dirty();
	}
}
const BVector& BTextual::down() const
{
	return member.down;
}

void BTextual::setOriginAlign(Align originAlign)
{
	if (member.originAlign != originAlign)
	{
		member.originAlign = originAlign;
		this->dirty();
	}
}
Align BTextual::originAlign() const
{
	return member.originAlign;
}

void BTextual::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		member.dirtyText = true;
		this->dirty();
	}
}
const BString& BTextual::text() const
{
	return member.text;
}

void BTextual::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		this->dirty();
	}
}
Align BTextual::align() const
{
	return member.align;
}

void BTextual::setFont(const BString& family, int size)
{
	if (!member.font)
	{
		BFont* font = new BFont();
		this->setFont(font);
	}
	if (member.font->family() != family)
	{
		member.font->setFamily(family);
		member.font->setSize(size);
		member.dirtyText = true;
		this->dirty();
	}
}
void BTextual::setFont(BFont* font)
{
	if (member.font != font)
	{
		member.font = font;
		member.dirtyText = true;
		this->dirty();
	}
}
const BFont* BTextual::font() const
{
	return member.font;
}
BFont* BTextual::font()
{
	return member.font;
}

void BTextual::setGlyphSize(BReal glyphSize)
{
	if (member.glyphSize != glyphSize)
	{
		member.glyphSize = glyphSize;
		member.dirtyText = true;
		this->dirty();
	}
}
BReal BTextual::glyphSize() const
{
	return member.glyphSize;
}

void BTextual::setColors(BColorArray* colors)
{
	if (member.colors != colors)
	{
		member.colors = colors;
		this->fresh();
	}
}
const BColorArray* BTextual::colors() const
{
	return member.colors;
}
BColorArray* BTextual::colors()
{
	return member.colors;
}

const BQuad& BTextual::quad() const
{
	return member.quad;
}

void BTextual::clear()
{
	member.text.clear();
	member.quad.reset();
	member.font = 0;
	member.items.clear();
}

bool BTextual::query(const BString& text, BValue& value) const
{
	return BShape::query(text, value);
}

BVector BTextual::inertia(BReal mass) const
{
	const BBox& box = this->box();
	BVector dimension = box.veca() + box.vecb() + box.vecc();
	dimension += this->margin();

	BReal lx = 2 * (dimension.x());
	BReal ly = 2 * (dimension.y());
	BReal lz = 2 * (dimension.z());

	BReal inex = mass / 12 * (ly * ly + lz * lz);
	BReal iney = mass / 12 * (lx * lx + lz * lz);
	BReal inez = mass / 12 * (lx * lx + ly * ly);

	return BVector(inex, iney, inez);
}
BVector BTextual::vertex(const BVector& direct) const
{
	const BBox& box = this->box();
	BVector dimension = box.veca() + box.vecb() + box.vecc();
	BReal dx = direct.x() >= 0 ? dimension.x() : -dimension.x();
	BReal dy = direct.y() >= 0 ? dimension.y() : -dimension.y();
	BReal dz = direct.z() >= 0 ? dimension.z() : -dimension.z();
	return BVector(dx, dy, dz);
}

void BTextual::refresh(BBox& box)
{
	if (member.dirtyText)
	{
		member.freshTextSize();
		member.dirtyText = false;
	}
	member.rebuild();
	BSpace space(member.quad.a());
	space.expand(member.quad.b());
	space.expand(member.quad.c());
	space.expand(member.quad.d());
	BReal half = bMax(space.width(), space.height()) / 2;
	box.reset(half, half, half);
}
void BTextual::render(BRender& render)
{
	if (member.items.size() > 0)
	{
		BColorArray* colors = member.colors;
		if (colors && colors->size() != member.items.size())
			colors = 0;
		render.unbind();
		glEnable(GL_TEXTURE_2D);
		const BVector& offset = member.textQuad.a();
		for (int i = 0; i < member.items.size(); i++)
		{
			CodeQuad& item = member.items[i];
			if (item.image)
			{
				BVector a = item.imageQuad.a() + offset;
				BVector b = item.imageQuad.b() + offset;
				BVector c = item.imageQuad.c() + offset;
				BVector d = item.imageQuad.d() + offset;

				if (colors)
				{
					const BColor& c = (*colors)[i];
					glColor4ub(c.r(), c.g(), c.b(), c.a());
				}

				if (texture_member(item.image)->dirty)
					item.image->flush();
				glBindTexture(GL_TEXTURE_2D, texture_member(item.image)->tbo);
				glBegin(GL_QUADS);
				glTexCoord2f(0, 0);		BOpenGL::glVertex(a.x(), a.y(), a.z());
				glTexCoord2f(0, 1);		BOpenGL::glVertex(b.x(), b.y(), b.z());
				glTexCoord2f(1, 1);		BOpenGL::glVertex(c.x(), c.y(), c.z());
				glTexCoord2f(1, 0);		BOpenGL::glVertex(d.x(), d.y(), d.z());
				glEnd();
			}
		}
		glBindTexture(GL_TEXTURE_2D, 0);
		glDisable(GL_TEXTURE_2D);
	}
}
